//
// Created by 原帅 on 2021/2/10.
//

#include <sys/time.h>
#include "IERunLoop.h"
#include <errno.h>
#include <unistd.h>
#include <stdio.h>

IERunLoop* IERunLoop::ieRunLoop = nullptr;

IERunLoop::IERunLoop() {
    this->interval = 1000*20;
}

IERunLoop::~IERunLoop() {

}

IERunLoop* IERunLoop::Singleton() {
    if (IERunLoop::ieRunLoop == nullptr) {
        IERunLoop::ieRunLoop = new IERunLoop();
    }
    return IERunLoop::ieRunLoop;
}

int IERunLoop::runloop() {
    for (auto init : inits) {
        init->runloopTask();
    }
    this->runing = true;
    while (this->runing){
        struct timeval start={0},end={0};
        gettimeofday(&start, nullptr);
        this->runloopOnce(this->onceTasksBeforeRunloop);
        this->runloopRepeat(this->beforeTasks);
        this->runloopOnce(this->onceTasks);
        this->runloopRepeat(this->tasks);
        this->runloopRepeat(this->afterTasks);
        this->runloopOnce(this->onceTasksAfterRunloop);
        gettimeofday(&end, nullptr);
        long usedTime = (end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);
//        printf("{IERunLoop}[%s]|Runloop once use:%ld microsecond\n",__func__ ,usedTime);
        long periodTime = this->interval - usedTime;
        if (usedTime < this->interval){
            usleep(periodTime);
//            printf("{IERunLoop}[%s]|Runloop done with total %ld microsecond\n",__func__ ,periodTime+usedTime);
        } else{
            printf("{IERunLoop}[%s]|Warrning >>>> Runloop once over time with total %ld microsecond\n",__func__ ,usedTime - this->interval);
        }

    }
    return errno;
}

void IERunLoop::stopRunloop() {
    this->runing = false;
}

void IERunLoop::addInits(IERunLoopInterface *anInterface) {
    this->inits.push_back(anInterface);
}

void IERunLoop::addTask(IERunLoopInterface *interface) {
    this->tasks.push_back(interface);
}

void IERunLoop::insertTask(int index, IERunLoopInterface *anInterface) {
    vector<IERunLoopInterface*>::iterator it = this->tasks.begin();
    this->tasks.insert(it + index, anInterface);
}

unsigned long IERunLoop::getInterval() const {
    return interval;
}

void IERunLoop::setInterval(unsigned long interval) {
    IERunLoop::interval = interval;
}

void IERunLoop::addTaskBefore(IERunLoopInterface *anInterface) {
    this->beforeTasks.push_back(anInterface);
}

void IERunLoop::insertTaskBefore(int index, IERunLoopInterface* anInterface) {
    vector<IERunLoopInterface*>::iterator it = this->beforeTasks.begin();
    this->beforeTasks.insert(it + index, anInterface);
}

void IERunLoop::addTaskAfter(IERunLoopInterface *anInterface) {
    this->afterTasks.push_back(anInterface);
}

void IERunLoop::insertTaskAfter(int index, IERunLoopInterface* anInterface) {
    vector<IERunLoopInterface*>::iterator it = this->afterTasks.begin();
    this->afterTasks.insert(it + index, anInterface);
}

void IERunLoop::pushOnceTask(IERunLoopInterface *anInterface) {
    onceTasks.push(anInterface);
}

void IERunLoop::pushOnceTaskOnBefore(IERunLoopInterface *anInterface) {
    onceTasksBeforeRunloop.push(anInterface);
}

void IERunLoop::pushOnceTaskOnAfter(IERunLoopInterface *anInterface) {
    onceTasksAfterRunloop.push(anInterface);
}

void IERunLoop::runloopRepeat(vector<IERunLoopInterface *> &tasks) {
    for (auto t : tasks){
        if (!this->runing){
            return;
        }
        t->runloopTask();
    }
}
void IERunLoop::runloopOnce(queue<IERunLoopInterface *> &tasks) {
    while (!tasks.empty()){
        if (!this->runing){
            return;
        }
        auto t = tasks.front();
        t->runloopTask();
        tasks.pop();
    }
}

void IERunLoop::removeTaskFromAllRunloop(IERunLoopInterface *anInterface) {
    this->removeTaskFromRunloop(anInterface,beforeTasks);
    this->removeTaskFromRunloop(anInterface,tasks);
    this->removeTaskFromRunloop(anInterface,afterTasks);
}

void IERunLoop::removeTaskFromRunloop(IERunLoopInterface *anInterface, vector<IERunLoopInterface *> tasks) {
//    vector<IERunLoopInterface*>::iterator it = tasks.begin();
    auto it = tasks.begin();
    while (it!=tasks.end()){
        if (*it == anInterface){
            tasks.erase(it);
        } else{
            it++;
        }
    }
}





